{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CCQY7jpBfMur"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "z6X9omPnfO_h"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2QQJJyDzqGRb"
      },
      "source": [
        "# Eager execution\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "B1xdylywqUSX"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/guide/eager\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Xem trên TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/vi/guide/eager.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Chạy trong Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/vi/guide/eager.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Xem nguồn trên GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/vi/guide/eager.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Tải notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EGjDcGxIqEfX"
      },
      "source": [
        "Tính năng eager execution của Tensorflow là một môi trường imperative programming cho phép thực hiện các phép tính toán ngay lập tức mà không cần xây dựng đồ thị: các phép tính toán trả về giá trị cụ thể thay vì xây dựng một đồ thị tính toán để thực hiện sau. Tính chất này giúp cho việc bắt đầu với Tensorflow và debug mô hình dễ dàng hơn, và nó cũng giúp giảm thiểu việc phải sử dụng boilerplate. Để theo dõi chỉ dẫn này, hãy thử chạy các đoạn code ở dưới trong một môi trường tương tác `python`.\n",
        "  \n",
        "Eager execution là một nền tảng học máy linh hoạt dành cho nghiên cứu và thí nghiệm bằng việc cung cấp:\n",
        "* *Một giao diện dễ hiểu*—Sắp xếp code của bạn một cách tự nhiên và sử dụng các cấu trúc dữ liệu của Python. Nhanh chóng thử nghiệm với các mô hình nhỏ và dữ liệu nhỏ.\n",
        "* *Debug dễ hơn*—Gọi các phép tính trực tiếp để giám sát mô hình đang chạy và kiểm tra thay đổi. Sử dụng các công cụ debug tiêu chuẩn của Python để báo cáo lỗi ngay lập tức.\n",
        "* *Control flow tự nhiên*—Sử dụng control flow của Python thay vì dùng graph control flow, đơn giản hóa chỉ dẫn kỹ thuật của các mô hình động.\n",
        "\n",
        "Eager execution hỗ trợ hầu hết các phép tính toán và tăng tốc GPU của Tensorflow.\n",
        "\n",
        "Chú ý: Vài mô hình có thể gặp hiện tượng tính toán nhiều hơn cần thiết khi dùng eager execution. Các cải thiện về hiệu suất đang được thực hiện, nhưng hãy [báo lỗi](https://github.com/tensorflow/tensorflow/issues) nếu như bạn tìm thấy một vấn đề và chia sẻ hệ thống kiểm chuẩn của bạn."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RBAeIwOMrYk8"
      },
      "source": [
        "## Cài đặt và sử dụng cơ bản"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "ByNsp4VqqEfa"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "import cProfile"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "48P3-8q4qEfe"
      },
      "source": [
        "Trong Tensorflow 2.0, eager execution mặc định được bật."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "7aFsD8csqEff"
      },
      "outputs": [],
      "source": [
        "tf.executing_eagerly()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "x_G1zZT5qEfh"
      },
      "source": [
        "Bây giờ bạn có thể chạy các phép tính toán của Tensorflow và kết quả sẽ được trả về ngay lập tức:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "9gsI54pbqEfj"
      },
      "outputs": [],
      "source": [
        "x = [[2.]]\n",
        "m = tf.matmul(x, x)\n",
        "print(\"hello, {}\".format(m))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ajFn6qsdqEfl"
      },
      "source": [
        "Việc bật eager execution thay đổi biểu hiện của các phép tính của TensorFlow - bây giờ chúng được tính và kết quả được trả về cho Python. Các đối tượng `tf.Tensor` tham chiếu tới các giá trị cụ thể thay vì tới các handle mang tính biểu tượng trỏ tới các đỉnh trong đồ thị tính toán. Vì không tồn tại một đồ thị tính toán để xây dựng và chạy trong một session, kiểm tra kết quả bằng `print()` hoặc một công cụ debug trở nên dễ dàng. Tính toán, in và kiểm trả giá trị của tensor không phá vỡ luồng tính toán đạo hàm.\n",
        "\n",
        "Eager execution hoạt động rất tốt với [NumPy](http://www.numpy.org/). Các phép tính của Numpy đều nhận tham số dưới dạng `tf.Tensor`. Các phép tính thuộc `tf.math` trong TensorFlow chuyển đổi các đối tượng Python và mảng Numpy thành đối tượng `tf.Tensor`. Phương thức `tf.Tensor.numpy` trả về kết quả của object dưới dạng Numpy `ndarray`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "sTO0_5TYqz1n"
      },
      "outputs": [],
      "source": [
        "a = tf.constant([[1, 2],\n",
        "                 [3, 4]])\n",
        "print(a)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Dp14YT8Gq4r1"
      },
      "outputs": [],
      "source": [
        "# Broadcasting support\n",
        "b = tf.add(a, 1)\n",
        "print(b)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "69p3waMfq8cQ"
      },
      "outputs": [],
      "source": [
        "# Overloading phép tính được hỗ trợ\n",
        "print(a * b)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "Ui025t1qqEfm"
      },
      "outputs": [],
      "source": [
        "# Sử dụng giá trị Numpy\n",
        "import numpy as np\n",
        "\n",
        "c = np.multiply(a, b)\n",
        "print(c)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Tq_aFRzWrCua"
      },
      "outputs": [],
      "source": [
        "# Lấy giá trị numpy từ một Tensor\n",
        "print(a.numpy())\n",
        "# => [[1 2]\n",
        "#     [3 4]]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "H08f9ss9qEft"
      },
      "source": [
        "## Luồng kiểm soát động\n",
        "\n",
        "Một lợi ích rất lớn của eager execution là tất cả tính năng của ngôn ngữ đang sử dụng đều có sẵn khi mô hình của bạn đang chạy. Ví dụ, rất dễ để viết [fizzbuzz](https://en.wikipedia.org/wiki/Fizz_buzz):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "0fudRMeUqEfu"
      },
      "outputs": [],
      "source": [
        "def fizzbuzz(max_num):\n",
        "  counter = tf.constant(0)\n",
        "  max_num = tf.convert_to_tensor(max_num)\n",
        "  for num in range(1, max_num.numpy()+1):\n",
        "    num = tf.constant(num)\n",
        "    if int(num % 3) == 0 and int(num % 5) == 0:\n",
        "      print('FizzBuzz')\n",
        "    elif int(num % 3) == 0:\n",
        "      print('Fizz')\n",
        "    elif int(num % 5) == 0:\n",
        "      print('Buzz')\n",
        "    else:\n",
        "      print(num.numpy())\n",
        "    counter += 1"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "P2cKknQWrJLB"
      },
      "outputs": [],
      "source": [
        "fizzbuzz(15)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7kA-aC3BqEfy"
      },
      "source": [
        "Đoạn code này có những điều kiện phụ thuộc vào giá trị của tensor và nó sẽ in những giá trị này vào lúc chạy."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8huKpuuAwICq"
      },
      "source": [
        "## Eager training"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mp2lCCZYrxHd"
      },
      "source": [
        "### Tính toán đạo hàm\n",
        "\n",
        "[Automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) rất hữu dụng cho việc cài đặt các thuật toán học máy như [truyền ngược](https://en.wikipedia.org/wiki/Backpropagation) cho việc huấn luyện các mạng thần kinh. Trong lúc thực hiện eager execution, sử dụng `tf.GradientTape` để theo dõi các phép tính cho việc tính toán đạo hàm sau đó.\n",
        "\n",
        "Bạn có thể dùng `tf.GradientTape` để huấn luyện và/hoặc tính toán đạo hàm trong eager. Nó đặc biệt hữu dụng cho các vòng lặp huấn luyện phức tạp.\n",
        "\n",
        "Bởi vì các phép tính khác nhau có thể xuất hiện trong mỗi lần gọi, tất cả các phép tính truyền xuôi đều được lưu lại trong một \"đoạn băng\". Để tính đạo hàm, chạy đoạn băng đó ngược lại và sau đó hủy nó. Một đối tượng `tf.GradientTape` chỉ có thể tính một đạo hàm; những lần gọi sau đó sẽ tạo ra runtime error."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "7g1yWiSXqEf-"
      },
      "outputs": [],
      "source": [
        "w = tf.Variable([[1.0]])\n",
        "with tf.GradientTape() as tape:\n",
        "  loss = w * w\n",
        "\n",
        "grad = tape.gradient(loss, w)\n",
        "print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vkHs32GqweYS"
      },
      "source": [
        "Ví dụ sau đây tạo ra một mô hình nhiều layer, mô hình này có thể phân loại các chữ cái viết tay trong dataset MNIST tiêu chuẩn. Nó hướng dẫn trình tối ưu hóa và các layer APIs cách xây dựng một mô hình có thể huấn luyện được trong một môi trường có eager execution."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "38kymXZowhhz"
      },
      "outputs": [],
      "source": [
        "# Lấy và format lại dữ liệu MNIST\n",
        "(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()\n",
        "\n",
        "dataset = tf.data.Dataset.from_tensor_slices(\n",
        "  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),\n",
        "   tf.cast(mnist_labels,tf.int64)))\n",
        "dataset = dataset.shuffle(1000).batch(32)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "rl1K8rOowmwT"
      },
      "outputs": [],
      "source": [
        "# Xây dựng mô hình\n",
        "mnist_model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu',\n",
        "                         input_shape=(None, None, 1)),\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),\n",
        "  tf.keras.layers.GlobalAveragePooling2D(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fvyk-HgGwxwl"
      },
      "source": [
        "Kể cả khi không huấn luyện, bạn vẫn có thể gọi mô hình và kiểm tra đầu ra khi đang ở trạng thái eager execution."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "BsxystjBwxLS"
      },
      "outputs": [],
      "source": [
        "for images,labels in dataset.take(1):\n",
        "  print(\"Logits: \", mnist_model(images[0:1]).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Y3PGa8G7qEgB"
      },
      "source": [
        "Mặc dù các mô hình keras có sẵn vòng lặp huấn luyện (bằng cách dùng phương thức `fit`), đôi lúc bạn cần nhiều khả năng chỉnh sửa hơn. Đây là ví dụ của một vòng lặp huấn luyện được xây dựng với eager:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "bzRhM7JDnaEG"
      },
      "outputs": [],
      "source": [
        "optimizer = tf.keras.optimizers.Adam()\n",
        "loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n",
        "\n",
        "loss_history = []"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tXaupYXRI2YM"
      },
      "source": [
        "Chú ý: Sử dụng các hàm assert trong `tf.debugging` để kiểm tra một điều kiện có được thỏa mãn hay không. Việc này có thể làm được cả trong eager và graph execution."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "DDHrigtiCIA4"
      },
      "outputs": [],
      "source": [
        "def train_step(images, labels):\n",
        "  with tf.GradientTape() as tape:\n",
        "    logits = mnist_model(images, training=True)\n",
        "    \n",
        "    # Thêm assert để kiểm tra hình dáng của output\n",
        "    tf.debugging.assert_equal(logits.shape, (32, 10))\n",
        "    \n",
        "    loss_value = loss_object(labels, logits)\n",
        "\n",
        "  loss_history.append(loss_value.numpy().mean())\n",
        "  grads = tape.gradient(loss_value, mnist_model.trainable_variables)\n",
        "  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "0m1xAXrmqEgJ"
      },
      "outputs": [],
      "source": [
        "def train(epochs):\n",
        "  for epoch in range(epochs):\n",
        "    for (batch, (images, labels)) in enumerate(dataset):\n",
        "      train_step(images, labels)\n",
        "    print ('Epoch {} finished'.format(epoch))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "C5dGz0p_nf4W"
      },
      "outputs": [],
      "source": [
        "train(epochs = 3)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "5vG5ql_2vYB5"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.plot(loss_history)\n",
        "plt.xlabel('Batch #')\n",
        "plt.ylabel('Loss [entropy]')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kKpOlHPLqEgl"
      },
      "source": [
        "Các đối tượng `tf.Variable` lưu trữ những đối tượng `tf.Tensor` có thể thay đổi - những giá trị được truy xuất trong quá trình huấn luyện để làm cho quá trình tính toán đạo hàm tự động dễ dàng hơn.\n",
        "\n",
        "Các nhóm biến có thể được đóng gói thành các layers hoặc các mô hình, cùng với những phương thức tính toán chúng. Xem [Các layers và mô hình keras tùy chỉnh](./keras/custom_layers_and_models.ipynb) nếu bạn muốn tìm hiểu thêm. SỰ khác biệt chính giữa layers và mô hình là mô hình có thêm các phương thức như `Model.fit`, `Model.evaluate` và `Model.save`.\n",
        "\n",
        "Ví dụ về tính toán đạo hàm tự động ở trên có thể được viết lại như sau:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2qXcPngYk8dN"
      },
      "outputs": [],
      "source": [
        "class Linear(tf.keras.Model):\n",
        "  def __init__(self):\n",
        "    super(Linear, self).__init__()\n",
        "    self.W = tf.Variable(5., name='weight')\n",
        "    self.B = tf.Variable(10., name='bias')\n",
        "  def call(self, inputs):\n",
        "    return inputs * self.W + self.B"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "nnQLBYmEqEgm"
      },
      "outputs": [],
      "source": [
        "# Một dataset nhỏ được xây dựng xung quanh hàm số 3 * x + 2\n",
        "NUM_EXAMPLES = 2000\n",
        "training_inputs = tf.random.normal([NUM_EXAMPLES])\n",
        "noise = tf.random.normal([NUM_EXAMPLES])\n",
        "training_outputs = training_inputs * 3 + 2 + noise\n",
        "\n",
        "# The loss function to be optimized\n",
        "def loss(model, inputs, targets):\n",
        "  error = model(inputs) - targets\n",
        "  return tf.reduce_mean(tf.square(error))\n",
        "\n",
        "def grad(model, inputs, targets):\n",
        "  with tf.GradientTape() as tape:\n",
        "    loss_value = loss(model, inputs, targets)\n",
        "  return tape.gradient(loss_value, [model.W, model.B])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Q7x1CDurl3IG"
      },
      "source": [
        "Tiếp theo:\n",
        "\n",
        "1. Tạo mô hình.\n",
        "2. Tìm đạo hàm của hàm mất mát theo các tham số của mô hình.\n",
        "3. Tìm một chiến lược để cập nhật các biến dựa trên đạo hàm."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "SbXJk0f2lztg"
      },
      "outputs": [],
      "source": [
        "model = Linear()\n",
        "optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)\n",
        "\n",
        "print(\"Initial loss: {:.3f}\".format(loss(model, training_inputs, training_outputs)))\n",
        "\n",
        "steps = 300\n",
        "for i in range(steps):\n",
        "  grads = grad(model, training_inputs, training_outputs)\n",
        "  optimizer.apply_gradients(zip(grads, [model.W, model.B]))\n",
        "  if i % 20 == 0:\n",
        "    print(\"Loss at step {:03d}: {:.3f}\".format(i, loss(model, training_inputs, training_outputs)))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "PV_dqer7pzSH"
      },
      "outputs": [],
      "source": [
        "print(\"Final loss: {:.3f}\".format(loss(model, training_inputs, training_outputs)))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "rvt_Wj3Tp0hm"
      },
      "outputs": [],
      "source": [
        "print(\"W = {}, B = {}\".format(model.W.numpy(), model.B.numpy()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rPjb8nRWqEgr"
      },
      "source": [
        "Chú ý: các biến sẽ còn tồn tại cho đến khi tham chiếu cuối cùng tới đối tượng Python được xóa và biến được phá hủy."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "scMjg6L6qEgv"
      },
      "source": [
        "### Lưu trữ file theo hình thức object\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Y-0ZcCcjwkux"
      },
      "source": [
        "Một mô hình `tf.keras.Model` có kèm theo một phương thức `save_weights` giúp bạn có thể dễ dàng tạo ra một checkpoint:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "oJrMX94PwD9s"
      },
      "outputs": [],
      "source": [
        "model.save_weights('weights')\n",
        "status = model.load_weights('weights')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2EfTjWV_wEng"
      },
      "source": [
        "Sử dụng `tf.train.Checkpoint` giúp bạn có thể nắm hoàn toàn quyền làm chủ quá trình này.\n",
        "\n",
        "Phần này của bài viết là một phiên bản ngắn gọn của [hướng dẫn huấn luyện bằng checkpoints](./checkpoint.ipynb).\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "7z5xRfdHzZOQ"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(10.)\n",
        "checkpoint = tf.train.Checkpoint(x=x)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "IffrUVG7zyVb"
      },
      "outputs": [],
      "source": [
        "x.assign(2.)   # Gán một giá trị mới cho biến và lưu.\n",
        "checkpoint_path = './ckpt/'\n",
        "checkpoint.save('./ckpt/')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "eMT9koCoqEgw"
      },
      "outputs": [],
      "source": [
        "x.assign(11.)  # Thay đổi giá trị của biến sau khi lưu.\n",
        "\n",
        "# Hồi phục lại giá trị từ checkpoint\n",
        "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))\n",
        "\n",
        "print(x)  # => 2.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vbFnP-yLqEgx"
      },
      "source": [
        "Để lưu và tải lại các mô hình, `tf.train.Checkpoint` lưu trữ trạng thái bên trong của các đối tượng mà không cần đến các biến ẩn. Để lưu lại trạng thái của một `model`, một `optimizer`, và một bước toàn cục, truyền chúng cho một biến `tf.train.Checkpoint`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "hWZHyAXMqEg0"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),\n",
        "  tf.keras.layers.GlobalAveragePooling2D(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])\n",
        "optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)\n",
        "checkpoint_dir = 'path/to/model_dir'\n",
        "if not os.path.exists(checkpoint_dir):\n",
        "  os.makedirs(checkpoint_dir)\n",
        "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")\n",
        "root = tf.train.Checkpoint(optimizer=optimizer,\n",
        "                           model=model)\n",
        "\n",
        "root.save(checkpoint_prefix)\n",
        "root.restore(tf.train.latest_checkpoint(checkpoint_dir))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "R-ITwkBCF6GJ"
      },
      "source": [
        "Lưu ý: Trong nhiều vòng lặp huấn luyện, các biến được tạo sau khi `tf.train.Checkpoint.restore` được gọi. Những biến này sẽ được lưu trữ ngay khi chúng được tạo, và assertions có thể được sử dụng để đảm bảo rằng một checkpoint đã được tải hoàn toàn. Hãy xem [hướng dẫn huấn luyện checkpoints](./checkpoint.ipynb) để xem chi tiết."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3yoD0VJ7qEg3"
      },
      "source": [
        "### Các phép đo hướng đối tượng\n",
        "\n",
        "`tf.keras.metrics` được lưu trữ dưới dạng objects. Cập nhật một phép đo bằng cách truyền dữ liệu mới cho callable, và nhận kết quả bằng phương thức `tf.keras.metrics.result`, ví dụ:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "9ccu0iAaqEg5"
      },
      "outputs": [],
      "source": [
        "m = tf.keras.metrics.Mean(\"loss\")\n",
        "m(0)\n",
        "m(5)\n",
        "m.result()  # => 2.5\n",
        "m([8, 9])\n",
        "m.result()  # => 5.5"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aB8qWtT955pI"
      },
      "source": [
        "### Tổng quan và TensorBoard\n",
        "\n",
        "[TensorBoard](https://tensorflow.org/tensorboard) là một công cụ trực quan hóa dùng để hiểu, debug và tối ưu quá trình huấn luyện mô hình. Nó sử dụng các sự kiện tổng quan được viết trong lúc chương trình đang thực hiện.\n",
        "\n",
        "Bạn có thể sử dụng `tf.summary` để lưu lại tổng quan của các biến trong eager execution. Ví dụ: để lưu lại tổng quan của `loss` một lần trong mỗi 100 bước huấn luyện"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "z6VInqhA6RH4"
      },
      "outputs": [],
      "source": [
        "logdir = \"./tb/\"\n",
        "writer = tf.summary.create_file_writer(logdir)\n",
        "\n",
        "steps = 1000\n",
        "with writer.as_default():  # hoặc gọi writer.set_as_default() trước vòng lặp\n",
        "  for i in range(steps):\n",
        "    step = i + 1\n",
        "    # Tình giá trị hàm mất mát với các hàm huấn luyện của bạn\n",
        "    loss = 1 - 0.001 * step\n",
        "    if step % 100 == 0:\n",
        "      tf.summary.scalar('loss', loss, step=step)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "08QQD2j36TaI"
      },
      "outputs": [],
      "source": [
        "!ls tb/"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xEL4yJe5qEhD"
      },
      "source": [
        "## Các chủ đề đạo hàm tự động nâng cao\n",
        "\n",
        "### Các mô hình động\n",
        "\n",
        "`tf.GradientTape` cũng có thể được sử dụng trong các mô hình động. Ví dụ sau đây cho thuật toán [tìm kiếm đường thẳng sử dụng quay lui](https://wikipedia.org/wiki/Backtracking_line_search) trong giống như code Numpy bình thường, ngoại trừ sự xuất hiện của gradient và việc ta có thể tính đạo hàm được mặc dù có cấu trúc điều khiển phức tạp:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "L518n5dkqEhE"
      },
      "outputs": [],
      "source": [
        "def line_search_step(fn, init_x, rate=1.0):\n",
        "  with tf.GradientTape() as tape:\n",
        "    # Các biến số được theo dõi tự động.\n",
        "    # Để tính gradient của một tensor, ta cần theo dõi (watch) nó.\n",
        "    tape.watch(init_x)\n",
        "    value = fn(init_x)\n",
        "  grad = tape.gradient(value, init_x)\n",
        "  grad_norm = tf.reduce_sum(grad * grad)\n",
        "  init_value = value\n",
        "  while value > init_value - rate * grad_norm:\n",
        "    x = init_x - rate * grad\n",
        "    value = fn(x)\n",
        "    rate /= 2.0\n",
        "  return x, value"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gieGOf_DqEhK"
      },
      "source": [
        "### Gradients tùy chỉnh\n",
        "\n",
        "Gradients tùy chỉnh là một cách để dễ dàng ghi đè gradients. Trong hàm forward, định nghĩa gradient theo đầu vào, đầu ra và các kết quả trung gian. Ví dụ, sau đây là một cách để dễ dạng cắt norm của gradient trong một lần lan truyền ngược:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "-OwwsWUAqEhK"
      },
      "outputs": [],
      "source": [
        "@tf.custom_gradient\n",
        "def clip_gradient_by_norm(x, norm):\n",
        "  y = tf.identity(x)\n",
        "  def grad_fn(dresult):\n",
        "    return [tf.clip_by_norm(dresult, norm), None]\n",
        "  return y, grad_fn"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JPLDHkF_qEhN"
      },
      "source": [
        "Gradients tùy chỉnh thường được sử dụng để cung cấp một gradient ổn định về tính toán cho một chuỗi các phép tính toán:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "24WiLROnqEhO"
      },
      "outputs": [],
      "source": [
        "def log1pexp(x):\n",
        "  return tf.math.log(1 + tf.exp(x))\n",
        "\n",
        "def grad_log1pexp(x):\n",
        "  with tf.GradientTape() as tape:\n",
        "    tape.watch(x)\n",
        "    value = log1pexp(x)\n",
        "  return tape.gradient(value, x)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "n8fq69r9-B-c"
      },
      "outputs": [],
      "source": [
        "# Tính toán gradient hoạt động bình thường ở x = 0.\n",
        "grad_log1pexp(tf.constant(0.)).numpy()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "_VFSU0mG-FSp"
      },
      "outputs": [],
      "source": [
        "# Tuy nhiên, tại x = 100 sẽ xảy ra thất bại vì sự thiếu ổn định tính toán.\n",
        "grad_log1pexp(tf.constant(100.)).numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-VcTR34rqEhQ"
      },
      "source": [
        "Ở đây, hàm `log1pexp` có thể được rút gọn thông qua phân tích vói một gradient tùy chỉnh. Cách cài đặt dưới đây sử dụng lại giá trị cho `tf.exp(x)`đã được tính trong quá trình lan truyền xuôi - giúp nó hiệu quả hơn thông qua việc loại bỏ các phép tính dư thừa."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "Q7nvfx_-qEhS"
      },
      "outputs": [],
      "source": [
        "@tf.custom_gradient\n",
        "def log1pexp(x):\n",
        "  e = tf.exp(x)\n",
        "  def grad(dy):\n",
        "    return dy * (1 - 1 / (1 + e))\n",
        "  return tf.math.log(1 + e), grad\n",
        "\n",
        "def grad_log1pexp(x):\n",
        "  with tf.GradientTape() as tape:\n",
        "    tape.watch(x)\n",
        "    value = log1pexp(x)\n",
        "  return tape.gradient(value, x)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "5gHPKMfl-Kge"
      },
      "outputs": [],
      "source": [
        "# Như trước, việc tính toán gradient hoạt động bình thường ở x = 0\n",
        "grad_log1pexp(tf.constant(0.)).numpy()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "u38MOfz3-MDE"
      },
      "outputs": [],
      "source": [
        "# Việc tính toán gradient cũng hoạt động ở x = 0\n",
        "grad_log1pexp(tf.constant(100.)).numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rnZXjfQzqEhV"
      },
      "source": [
        "## Hiệu nặng\n",
        "\n",
        "Việc tính toán được tự động chuyển cho các GPU trong quá trình eager execution. Nếu bạn muốn nắm quyền kiểm soát khi một phép tính toán được thực hiện, bạn có thể  để nó trong một khối lệnh `tf.device(/gpu:0')` (hoặc là phiên bản tương ứng với CPU):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "Ac9Y64H-qEhX"
      },
      "outputs": [],
      "source": [
        "import time\n",
        "\n",
        "def measure(x, steps):\n",
        "  # TensorFlow khỏi tạo một GPU lần đầu tiên được sử dụng, ta không tính nó vào việc đếm thời gian\n",
        "  tf.matmul(x, x)\n",
        "  start = time.time()\n",
        "  for i in range(steps):\n",
        "    x = tf.matmul(x, x)\n",
        "  # tf.matmul có thể trả về trước khi thực hiện một phép nhân ma trận\n",
        "  # (vd có thể trả về trước khi phép tính được cho vào một stream CUDA).\n",
        "  # Lần gọi x.numpy() ở dưới sẽ đảm bảo rằng tất cả phép toán đang đợi\n",
        "  # đều đã được thực hiện (và cũng sẽ sao chép kết quả đến bộ nhớ của máy),\n",
        "  # nên chúng ta đang bao gồm thời gian nhiều hơn một chút so với thời gian của\n",
        "  # phép nhân ma trận\n",
        "  _ = x.numpy()\n",
        "  end = time.time()\n",
        "  return end - start\n",
        "\n",
        "shape = (1000, 1000)\n",
        "steps = 200\n",
        "print(\"Time to multiply a {} matrix by itself {} times:\".format(shape, steps))\n",
        "\n",
        "# Chạy trên CPU:\n",
        "with tf.device(\"/cpu:0\"):\n",
        "  print(\"CPU: {} secs\".format(measure(tf.random.normal(shape), steps)))\n",
        "\n",
        "# Chạy trên GPU, nếu có thể:\n",
        "if tf.config.experimental.list_physical_devices(\"GPU\"):\n",
        "  with tf.device(\"/gpu:0\"):\n",
        "    print(\"GPU: {} secs\".format(measure(tf.random.normal(shape), steps)))\n",
        "else:\n",
        "  print(\"GPU: not found\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RLw3IS7UqEhe"
      },
      "source": [
        "Một đối tượng `tf.Tensor` có thể được sao chép tới một thiết bị khác để thực hiện phép tính toán của nó:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab": {},
        "colab_type": "code",
        "id": "ny6LX2BVqEhf"
      },
      "outputs": [],
      "source": [
        "if tf.config.experimental.list_physical_devices(\"GPU\"):\n",
        "  x = tf.random.normal([10, 10])\n",
        "\n",
        "  x_gpu0 = x.gpu()\n",
        "  x_cpu = x.cpu()\n",
        "\n",
        "  _ = tf.matmul(x_cpu, x_cpu)    # Chạy trên CPU\n",
        "  _ = tf.matmul(x_gpu0, x_gpu0)  # Chạy trên GPU:0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "oA_qaII3-p6c"
      },
      "source": [
        "### Các Benchmark\n",
        "\n",
        "Với các mô hình nặng về mặt tính toán, ví dụ như [ResNet50](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples/resnet50) huấn luyện trên GPU thì hiệu năng của eager execution sẽ tương đương với thực hiện bằng `tf.function`. Tuy nhiên, khoảng cách này sẽ tăng lên khi với mô hình sử dụng ít phép tính toán hơn và vẫn còn rất nhiều việc phải làm để tối ưu các hot code paths cho mô hình với nhiều phép tính toán nhỏ.\n",
        "\n",
        "## Làm việc với các functions\n",
        "\n",
        "Mặc dù eager execution giúp cho việc phát triển và debug tương tác hóa hơn, graph execution theo kiểu của TensorFlow 1.x có lợi thế trong huấn luyện được phân phối và triển khai sản phẩm. Để làm giảm khoảng cách này, TensorFlow 2.0 giới thiệu các `function` thông qua API `tf.function`. Để biết thêm thông tin, hãy xem hướng dẫn của [tf.function](./function.ipynb)"
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "eager.ipynb",
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
